package com.hgx.common.util.http;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.hgx.common.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.DefaultSchemePortResolver;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Description: HttpClient工具类
 * Author: han gui xian
 * Create: 2019/10/17 15:22
 */
@Slf4j
@SuppressWarnings("Duplicates")
public class HttpClientUtils {

    /**
     * 请求的相关配置
     */
    private static RequestConfig REQUEST_CONFIG;
    /**
     * connectionManager
     */
    private static PoolingHttpClientConnectionManager CONNECTION_MANAGER;
    /**
     * httpClient
     */
    private static HttpClient CLIENT;

    static {
        // read config from system.properties
        int maxTotalConnections = 200;
        int defaultMaxPerRoute = 80;
        int connectionTimeOut = 60 * 1000, socketTimeOut = 60 * 1000;

        CONNECTION_MANAGER = new PoolingHttpClientConnectionManager();
        CONNECTION_MANAGER.setMaxTotal(maxTotalConnections);
        CONNECTION_MANAGER.setDefaultMaxPerRoute(defaultMaxPerRoute);

        REQUEST_CONFIG = RequestConfig.custom()
                .setConnectTimeout(connectionTimeOut).setSocketTimeout(socketTimeOut)
                .build();
    }

    /**
     * 获取 httpClient
     *
     * @return org.apache.http.client.HttpClient
     */
    private static HttpClient getHttpClient() {
        if (CLIENT == null) {
            synchronized (HttpClientUtils.class) {
                if (CLIENT == null) {
                    CLIENT = HttpClientBuilder.create().setConnectionManager(CONNECTION_MANAGER)
                            .setSchemePortResolver(new DefaultSchemePortResolver())
                            .setSSLSocketFactory(SSLConnectionSocketFactory.getSocketFactory())
                            .setDefaultRequestConfig(REQUEST_CONFIG).build();
                }
            }
        }
        return CLIENT;
    }

    /**
     * 配置给定的 httpClient 委托到当前 HttpClientUtils
     * 当配置不局限于 当前默认配置的时候, 使用
     *
     * @param client client
     */
    public static void setHttpClient(HttpClient client) {
        if (client == null) {
            return;
        }

        synchronized (HttpClientUtils.class) {
            CLIENT = client;
        }
    }

    /**
     * 发送一个 get 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param params      params
     * @param respCharset respCharset
     * @return java.lang.String
     * @throws CommonException 通用
     */
    public static String get(String url, Map<String, String> headers, Map<String, String> params, String respCharset) throws CommonException {
        log.info(" 发送 http-get 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params));
        HttpClient httpclient = getHttpClient();
        HttpGet get = null;
        String queryString = encapQueryString(params);

        try {
            String nextSep = url.contains("?") ? "&" : "?";
            String reqUrl = url;
            if (!StringUtils.isBlank(queryString)) {
                reqUrl = url + nextSep + queryString;
            }
            get = new HttpGet(reqUrl);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                get.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(get);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-get 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params);
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }

            String result = EntityUtils.toString(resp.getEntity(), respCharset);
            EntityUtils.consume(resp.getEntity());
            return result;
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-get 请求, 发生异常参数 url : " + url + ", headers : " + JSON.toJSONString(headers)
                    + ", params : " + JSON.toJSONString(params) + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            if (get != null) {
                get.releaseConnection();
            }
        }
    }

    public static String get(String url) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return get(url, Collections.emptyMap(), null, charset);
    }

    public static String get(String url, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return get(url, Collections.emptyMap(), params, charset);
    }

    public static String get(String url, Map<String, String> headers, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return get(url, headers, params, charset);
    }

    public static String get(String url, Map<String, String> params, String respCharset) throws CommonException {
        return get(url, Collections.emptyMap(), params, respCharset);
    }

    /**
     * 发送一个 post 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param params      params
     * @param reqCharset  reqCharset
     * @param respCharset respCharset
     * @return java.lang.String
     * @throws CommonException 异常
     */
    public static String post(String url, Map<String, String> headers, Map<String, String> params, String reqCharset, String respCharset) throws CommonException {
        log.info(" 发送 http-post 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params));
        HttpClient httpclient = getHttpClient();
        HttpPost post = new HttpPost(url);

        List<NameValuePair> paramKvs = new ArrayList<>(params.size());
        for (Map.Entry<String, String> entry : params.entrySet()) {
            paramKvs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        try {
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(paramKvs, reqCharset);
            post.setEntity(formEntity);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                post.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(post);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-post 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params);
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }
            String result = EntityUtils.toString(resp.getEntity(), respCharset);
            EntityUtils.consume(resp.getEntity());
            return result;
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-post 请求, 发生异常 参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params) + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            post.releaseConnection();
        }
    }

    public static String post(String url, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return post(url, Collections.emptyMap(), params, charset, charset);
    }

    public static String post(String url, Map<String, String> headers, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return post(url, headers, params, charset, charset);
    }

    public static String post(String url, Map<String, String> params, String reqCharset, String respCharset) throws CommonException {
        return post(url, Collections.emptyMap(), params, reqCharset, respCharset);
    }

    /**
     * 发送一个 post 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param requestBody requestBody
     * @param reqCharset  reqCharset
     * @param respCharset respCharset
     * @return java.lang.String
     */
    public static String postByRequestBody(String url, Map<String, String> headers, String requestBody, String reqCharset, String respCharset) {
        log.info(" 发送 http-post 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody);
        HttpClient httpclient = getHttpClient();
        HttpPost post = new HttpPost(url);

        try {
            StringEntity entity = new StringEntity(requestBody, reqCharset);
            entity.setContentEncoding("UTF-8");
            entity.setContentType("application/json");
            post.setEntity(entity);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                post.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(post);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-post 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody;
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }

            String result = EntityUtils.toString(resp.getEntity(), respCharset);
            EntityUtils.consume(resp.getEntity());
            return result;
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-post 请求, 发生异常 参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            post.releaseConnection();
        }
    }

    public static String postByRequestBody(String url, String requestBody) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postByRequestBody(url, Collections.emptyMap(), requestBody, charset, charset);
    }

    public static String postByRequestBody(String url, Map<String, String> headers, String requestBody) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postByRequestBody(url, headers, requestBody, charset, charset);
    }

    public static String postByRequestBody(String url, String requestBody, String reqCharset, String respCharset) throws CommonException {
        return postByRequestBody(url, Collections.emptyMap(), requestBody, reqCharset, respCharset);
    }

    // ---------------------------------- $methodForInputStream --------------------------------

    /**
     * 发送一个 get 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param params      params
     * @param respCharset respCharset
     * @return java.lang.String
     * @throws CommonException 异常
     */
    public static InputStream getForInputStream(String url, Map<String, String> headers, Map<String, String> params, String respCharset) throws CommonException {
        log.info(" 发送 http-get 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params));
        HttpClient httpclient = getHttpClient();
        HttpGet get = null;
        String queryString = encapQueryString(params);

        try {
            String nextSep = url.contains("?") ? "&" : "?";
            String reqUrl = url;
            if (!StringUtils.isBlank(queryString)) {
                reqUrl = url + nextSep + queryString;
            }
            get = new HttpGet(reqUrl);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                get.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(get);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-get 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params);
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }

            return resp.getEntity().getContent();
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-get 请求, 发生异常 参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + queryString + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            if (get != null) {
                get.releaseConnection();
            }
        }
    }

    public static InputStream getForInputStream(String url, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return getForInputStream(url, Collections.emptyMap(), params, charset);
    }

    public static InputStream getForInputStream(String url, Map<String, String> headers, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return getForInputStream(url, headers, params, charset);
    }

    public static InputStream getForInputStream(String url, Map<String, String> params, String respCharset) throws CommonException {
        return getForInputStream(url, Collections.emptyMap(), params, respCharset);
    }

    /**
     * 发送一个 post 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param params      params
     * @param reqCharset  reqCharset
     * @param respCharset respCharset
     * @return java.lang.String
     * @throws CommonException 异常
     */
    public static InputStream postForInputStream(String url, Map<String, String> headers, Map<String, String> params, String reqCharset, String respCharset) throws CommonException {
        log.info(" 发送 http-post 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params));
        HttpClient httpclient = getHttpClient();
        HttpPost post = new HttpPost(url);

        List<NameValuePair> paramKvs = new ArrayList<>(params.size());
        for (Map.Entry<String, String> entry : params.entrySet()) {
            paramKvs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        try {
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(paramKvs, reqCharset);
            post.setEntity(formEntity);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                post.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(post);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-post 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params);
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }
            return resp.getEntity().getContent();
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-post 请求, 发生异常 参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + JSON.toJSONString(params) + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            post.releaseConnection();
        }
    }

    public static InputStream postForInputStream(String url, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postForInputStream(url, Collections.emptyMap(), params, charset, charset);
    }

    public static InputStream postForInputStream(String url, Map<String, String> headers, Map<String, String> params) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postForInputStream(url, headers, params, charset, charset);
    }

    public static InputStream postForInputStream(String url, Map<String, String> params, String reqCharset, String respCharset) throws CommonException {
        return postForInputStream(url, Collections.emptyMap(), params, reqCharset, respCharset);
    }

    /**
     * 发送一个 post 请求, 使用给定的 header, params
     *
     * @param url         url
     * @param headers     headers
     * @param requestBody requestBody
     * @param reqCharset  reqCharset
     * @param respCharset respCharset
     * @return java.lang.String
     * @throws CommonException 异常
     */
    public static InputStream postForInputStreamByRequestBody(String url, Map<String, String> headers, String requestBody, String reqCharset, String respCharset) throws CommonException {
        log.info(" 发送 http-post 请求, url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody);
        HttpClient httpclient = getHttpClient();
        HttpPost post = new HttpPost(url);

        try {
            StringEntity entity = new StringEntity(requestBody);
            post.setEntity(entity);
            for (Map.Entry<String, String> header : headers.entrySet()) {
                post.addHeader(new BasicHeader(header.getKey(), header.getValue()));
            }

            HttpResponse resp = httpclient.execute(post);
            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                String ex = " 发送 http-post 请求, 拿到了错误码 : " + resp.getStatusLine().getStatusCode() + ", " +
                        "参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody;
                log.error(ex);
                throw new CommonException("network_" + resp.getStatusLine().getStatusCode(), ex);
            }

            return resp.getEntity().getContent();
        } catch (CommonException e) {
            throw e;
        } catch (Exception e) {
            String ex = " 发送 http-post 请求, 发生异常 参数 url : " + url + ", headers : " + JSON.toJSONString(headers) + ", params : " + requestBody + ", e : " + e;
            log.error(ex);
            throw new CommonException("999", ex);
        } finally {
            post.releaseConnection();
        }
    }

    public static InputStream postForInputStreamByRequestBody(String url, String requestBody) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postForInputStreamByRequestBody(url, Collections.emptyMap(), requestBody, charset, charset);
    }

    public static InputStream postForInputStreamByRequestBody(String url, Map<String, String> headers, String requestBody) throws CommonException {
        String charset = Charset.defaultCharset().name();
        return postForInputStreamByRequestBody(url, headers, requestBody, charset, charset);
    }

    public static InputStream postForInputStreamByRequestBody(String url, String requestBody, String reqCharset, String respCharset) throws CommonException {
        return postForInputStreamByRequestBody(url, Collections.emptyMap(), requestBody, reqCharset, respCharset);
    }

    // ---------------------------------- 辅助方法 --------------------------------

    /**
     * 封装给定的参数集合的查询字符串表示
     *
     * @param params params
     * @return java.lang.String
     */
    private static String encapQueryString(Map<String, String> params) {
        if (CollectionUtil.isEmpty(params)) {
            return "";
        }
        StringBuilder queryStrBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            queryStrBuilder.append(entry.getKey()).append("=").append(entry.getValue());
            queryStrBuilder.append("&");
        }

        String str = queryStrBuilder.toString();
        if (str.endsWith("&")) {
            return str.substring(0, str.length() - "&".length());
        }
        return str;
    }


}
